use shq_common::prelude::{get_expr_context_from_table, FlowState, SubmitType};

use crate::{
    config::{get_workflow_config, CallBack, NoExecutorTactics, Workflow},
    executor::{cache::FlowCache, get_executor},
    extend::TaskCheckCallback,
    flow_run_step::{delete_step_user_state, StepInstance},
    utils::table_data_to_his,
};

use super::database::{flow_activate, flow_finish, flow_suspend, flow_to_step};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub(crate) struct FlowInstance {
    pub run_id: u64,
    pub state: FlowState,
    pub wfid: u32,
    pub step_id: u8,
    pub version: u8,
}
impl FlowInstance {
    pub async fn from(run_id: u64) -> anyhow::Result<Self> {
        let mut fuc = FlowCache::from(run_id)?;
        let fi = fuc.get_flow_info().await?;
        let ins = Self {
            run_id,
            state: fi.state.into(),
            wfid: fi.wfid,
            step_id: fi.step_id,
            version: fi.version,
        };
        Ok(ins)
    }
    pub async fn submit(
        &self,
        user_id: u32,
        st: SubmitType,
        feed_back: Option<String>,
        is_manager: bool,
        tx: &mut sqlx::Transaction<'_, sqlx::MySql>,
    ) -> anyhow::Result<()> {
        if self.state == FlowState::Completed {
            anyhow::bail!("流程已经执行完成，不能提交");
        }
        if st == SubmitType::Activate && !is_manager {
            anyhow::bail!("非管理员不能激活流程");
        }
        if st == SubmitType::Completed && !is_manager {
            anyhow::bail!("非管理员不能强制完成流程");
        }
        if st != SubmitType::Activate && self.state == FlowState::Suspended {
            anyhow::bail!("流程已经中止，不能提交");
        }
        let mut feed_back = feed_back;
        if st == SubmitType::Completed {
            if feed_back.is_none() {
                feed_back = Some("强制完成".to_owned());
            }
        }
        let conf = get_workflow_config(self.wfid, self.version).ok_or(anyhow::anyhow!(
            "wfid{}:version{} not found",
            self.wfid,
            self.version
        ))?;
        let step_conf = conf.get_step(self.step_id).ok_or(anyhow::anyhow!(
            "wfid{}:version{} not found step_id{}",
            self.wfid,
            self.version,
            self.step_id
        ))?;
        if let Some(ccb) = step_conf.check_call_back.as_ref() {
            let tccb = TaskCheckCallback::new(&ccb);
            tccb.check(self.run_id, self.step_id, st).await?;
        }
        let mut si = StepInstance::from(self.run_id, self.step_id, step_conf.is_vote,Some(tx)).await?;
        let mut fuc = FlowCache::from(self.run_id)?;
        let mut to_si: Option<StepInstance> = None;
        let mut next_auto_submit = false;
        match st {
            SubmitType::SendNext | SubmitType::Completed => {
                let finish = si
                    .seed_next(
                        user_id,
                        step_conf.counter_sign_type.clone(),
                        feed_back,
                        is_manager,
                        tx,
                    )
                    .await?;
                let is_last_step = self.step_id == conf.steps.len() as u8;
                if finish {
                    if step_conf.is_vote {
                        let is_ok = si.is_vote_ok(step_conf.counter_sign_type.clone()).await?;
                        if is_ok {
                            (to_si, next_auto_submit) = self
                                .to_next(
                                    user_id,
                                    is_last_step,
                                    &mut fuc,
                                    conf.finish_call_back.as_ref(),
                                    conf,
                                    step_conf.is_vote,
                                    &st,
                                    tx,
                                )
                                .await?;
                        } else {
                            let back_step_id = step_conf.back_to_steps[0];
                            to_si = self
                                .to_back(back_step_id, &mut fuc, step_conf.is_vote, tx)
                                .await?;
                        }
                    } else {
                        (to_si, next_auto_submit) = self
                            .to_next(
                                user_id,
                                is_last_step,
                                &mut fuc,
                                conf.finish_call_back.as_ref(),
                                conf,
                                step_conf.is_vote,
                                &st,
                                tx,
                            )
                            .await?;
                    }
                }
            }
            SubmitType::SendBack => {
                let wf = get_workflow_config(self.wfid, self.version).ok_or(anyhow::anyhow!(
                    "流程wfid{}:version{}不存在",
                    self.wfid,
                    self.version
                ))?;
                let to_step_id = if let Some(step) = wf.get_step(self.step_id) {
                    if step.back_to_steps.is_empty() {
                        self.step_id - 1
                    } else {
                        step.back_to_steps[0]
                    }
                } else {
                    self.step_id - 1
                };
                if !step_conf.back_to_steps.contains(&to_step_id) {
                    anyhow::bail!("流程配置中不允许退回到步骤{to_step_id}");
                }
                let finish = si
                    .seed_back(user_id, step_conf.counter_sign_type.clone(), feed_back, tx)
                    .await?;
                if finish {
                    if step_conf.is_vote {
                        let is_last_step = self.step_id == conf.steps.len() as u8;
                        let is_ok = si.is_vote_ok(step_conf.counter_sign_type.clone()).await?;
                        if is_ok {
                            (to_si, next_auto_submit) = self
                                .to_next(
                                    user_id,
                                    is_last_step,
                                    &mut fuc,
                                    conf.finish_call_back.as_ref(),
                                    conf,
                                    step_conf.is_vote,
                                    &st,
                                    tx,
                                )
                                .await?;
                        } else {
                            to_si = self
                                .to_back(to_step_id, &mut fuc, step_conf.is_vote, tx)
                                .await?;
                        }
                    } else {
                        to_si = self
                            .to_back(to_step_id, &mut fuc, step_conf.is_vote, tx)
                            .await?;
                    }
                }
            }
            SubmitType::TakeBack => {
                if !step_conf.can_take_back {
                    anyhow::bail!("流程配置中当前步骤不能允许取回");
                }
                if self.state == FlowState::Completed {
                    anyhow::bail!("流程已经执行完成，不能取回");
                }
                si.take_back(user_id, feed_back, tx).await?;
                let next_step_id = self.step_id - 1;
                if next_step_id == 0 {
                    anyhow::bail!("第一个步骤不能取回");
                }
                let mut next_si =
                    StepInstance::from(self.run_id, next_step_id, step_conf.is_vote, Some(tx)).await?;
                next_si.start(tx).await?;
                to_si = Some(next_si);
                flow_to_step(self.run_id, next_step_id, tx).await?;
                fuc.set_flow_info(self.wfid, next_step_id, 0, self.version)?;
            }
            SubmitType::Suspend => {
                if !step_conf.can_suspend {
                    anyhow::bail!("流程配置中当前步骤不能允许中止");
                }
                si.suspend(user_id, feed_back, tx).await?;
                flow_suspend(self.run_id, tx).await?;
                fuc.set_flow_info(self.wfid, self.step_id, 2, self.version)?;
            }
            SubmitType::Activate => {
                if !step_conf.can_suspend {
                    anyhow::bail!("流程配置中当前步骤不允许中止,所以也不允许激活");
                }
                if self.state == FlowState::Completed {
                    anyhow::bail!("流程已经执行完成，不能激活");
                }
                si.start(tx).await?;
                flow_activate(self.run_id, tx).await?;
                fuc.set_flow_info(self.wfid, self.step_id, 0, self.version)?;
            }
        }
        si.cache_user_infos()?;
        if let Some(mut to_si) = to_si {
            to_si.cache_user_infos()?;
            if next_auto_submit {
                let new_fi = FlowInstance::from(self.run_id).await?;
                Box::pin(new_fi.submit(
                    0,
                    SubmitType::SendNext,
                    Some("系统自动提交".to_owned()),
                    true,
                    tx,
                ))
                .await
                .or_else(|err| {
                    // 自动提交失败，删除缓存
                    fuc.del_step_users(si.get_step_id())?;
                    fuc.del_step_users(to_si.get_step_id())?;
                    fuc.del_flow_info()?;
                    Err(err)
                })?;
            }
        }
        Ok(())
    }
    async fn to_next(
        &self,
        user_id: u32,
        is_last_step: bool,
        fuc: &mut FlowCache,
        finish_call_back: Option<&CallBack>,
        wconf: &Workflow,
        is_vote: bool,
        st: &SubmitType,
        tx: &mut sqlx::Transaction<'_, sqlx::MySql>,
    ) -> anyhow::Result<(Option<StepInstance>, bool)> {
        if is_last_step {
            flow_finish(self.run_id, self.step_id, tx)
                .await
                .or_else(|err| {
                    fuc.del_step_users(self.step_id)?;
                    Err(err)
                })?;
            if let Some(fcb) = finish_call_back {
                fcb.exec(self.run_id, user_id, tx).await?;
            }
            if wconf.has_history_table {
                let where_cause = format!("{} = {}", wconf.flow_run_id_col, self.run_id);
                table_data_to_his(&wconf.table_name, &where_cause, tx).await?;
                let where_cause = format!("run_id = {}", self.run_id);
                table_data_to_his("flow_run", &where_cause, tx).await?;
                table_data_to_his("flow_run_step", &where_cause, tx).await?;
            }
            fuc.set_flow_info(self.wfid, self.step_id, 1, self.version)?;
            return Ok((None, false));
        } else {
            let next_step_id = self.step_id + 1;
            let mut next_auto_submit = false;
            let sconf = wconf
                .get_step(next_step_id)
                .ok_or(anyhow::anyhow!("流程配置中不存在步骤{}", next_step_id))?;
            if let Some(ep) = sconf.expr_parser.as_ref() {
                let where_cause = format!("{} = {}", wconf.flow_run_id_col, self.run_id);
                let context = get_expr_context_from_table(
                    &wconf.table_name,
                    ep.get_col_names(),
                    &where_cause,
                )
                .await?;
                let val = ep.eval(context)?;
                let is_ok = val.as_boolean()?;
                if !is_ok {
                    next_auto_submit = true;
                }
            }
            let users = get_executor(self.run_id, next_step_id, Some(tx)).await?;
            //如果上一次是系统自动提交，则删除该步骤的用户状态
            if users.iter().all(|e| e.user_id == 0) {
                let mut fuc = FlowCache::from(self.run_id)?;
                fuc.del_step_users(next_step_id)?;
                delete_step_user_state(self.run_id, next_step_id, &users, tx).await?;
            }
            let mut next_si = StepInstance::from(self.run_id, next_step_id, is_vote, Some(tx)).await?;
            if !next_auto_submit {
                if wconf.is_sign_once {
                    let es = next_si.get_executor();
                    let is_has = es.iter().any(|e| e.user_id == user_id);
                    if is_has {
                        next_auto_submit = true;
                    }
                }
                if next_si.get_executor().is_empty() {
                    match sconf.no_executor_tractics {
                        NoExecutorTactics::Auto => next_auto_submit = true,
                        _ => {
                            anyhow::bail!("步骤{}:{}没有执行人", next_step_id, sconf.name);
                        }
                    }
                }
                next_si.start(tx).await?;
            }
            flow_to_step(self.run_id, next_step_id, tx).await?;
            fuc.set_flow_info(self.wfid, next_step_id, 0, self.version)?;
            if st == &SubmitType::Completed {
                next_auto_submit = true;
            }
            return Ok((Some(next_si), next_auto_submit));
        }
    }
    async fn to_back(
        &self,
        back_step_id: u8,
        fuc: &mut FlowCache,
        is_vote: bool,
        tx: &mut sqlx::Transaction<'_, sqlx::MySql>,
    ) -> anyhow::Result<Option<StepInstance>> {
        let users = get_executor(self.run_id, back_step_id, Some(tx)).await?;
        //如果上一步骤没有执行人，则删除该步骤的用户状态
        if users.iter().all(|e| e.user_id == 0) {
            let mut fuc = FlowCache::from(self.run_id)?;
            fuc.del_step_users(back_step_id)?;
            delete_step_user_state(self.run_id, back_step_id, &users, tx).await?;
        }
        let mut back_si = StepInstance::from(self.run_id, back_step_id, is_vote,Some(tx)).await?;
        back_si.start(tx).await?;
        flow_to_step(self.run_id, back_step_id, tx).await?;
        fuc.set_flow_info(self.wfid, back_step_id, 0, self.version)?;
        Ok(Some(back_si))
    }
}
